Vite handles HMR differently for each asset type: CSS updates via style injection without page reload, JavaScript via precise module replacement, and frameworks through dedicated plugins that preserve component state
Vite's Hot Module Replacement (HMR) system is not a one-size-fits-all mechanism—it adapts its behavior based on the type of file being modified. This specialization is what makes Vite's HMR feel instantaneous across different asset types. CSS changes can be applied by simply swapping stylesheets, JavaScript updates require more careful module graph invalidation, and framework components need sophisticated state preservation through dedicated plugins like @vitejs/plugin-react or @vitejs/plugin-vue [citation:1].
CSS HMR is the simplest and fastest case. When a CSS file changes, Vite's CSS plugin injects the updated styles directly into the page using JavaScript. The client runtime calls updateStyle() to replace the existing style element's content with the new CSS, or creates a new style element if needed. This operation is extremely fast because it doesn't require any JavaScript module re-evaluation—just a DOM update. The old style element is removed via removeStyle() during cleanup [citation:5]. CSS HMR works for regular CSS, CSS modules, and preprocessors like SCSS or Less, all of which are converted to CSS during the transformation step [citation:1].
JavaScript HMR is more complex because changes can affect module dependencies. When a JavaScript file changes, Vite's server identifies the exact modules affected via its module graph. It then sends a WebSocket update containing the changed module path and timestamp. The client dynamically re-imports the module with a cache-busting query parameter (?t=timestamp). If the module has an accept handler, that handler receives the new module exports. If not, Vite walks up the import chain looking for an accepting parent—if none exists, it falls back to a full page reload [citation:1]. This approach ensures updates remain fast regardless of application size because only the changed module and its direct dependents are reprocessed [citation:9].
React requires special handling because simply replacing a component module would lose its internal state. Vite's React plugin (@vitejs/plugin-react) integrates React Fast Refresh, which preserves component state across edits. The plugin works by separating component definitions from their instances—when a component file changes, only the render function is swapped out while the React Fiber nodes (component instances and state) remain intact [citation:7]. The plugin enables Fast Refresh by default with fastRefresh: true, and it works seamlessly with both function components and hooks. Error boundaries can catch HMR-related errors, and the error overlay provides immediate feedback [citation:7].
Vue's HMR implementation through @vitejs/plugin-vue handles the three parts of a Vue SFC separately. When a <template> changes, Vue's compiler generates a new render function and hot-replaces it while preserving the component instance. When <script> changes, the plugin attempts to merge new options with the existing instance—if successful, state is preserved; otherwise, it falls back to reloading the component. CSS changes within <style> blocks follow the same path as regular CSS HMR. This three-tiered approach ensures maximum state preservation while keeping updates fast [citation:1].
Svelte's HMR through @sveltejs/vite-plugin-svelte takes yet another approach. Svelte components compile to vanilla JavaScript classes, and HMR is handled by replacing the component class while preserving instances where possible. The Svelte plugin maintains a registry of component instances and, when a component changes, it creates a new class and updates existing instances to use the new class's methods while keeping their state. This is more complex than React or Vue because Svelte's compilation model produces framework-specific runtime code that must be carefully hot-swapped.
CSS: Injects/updates style elements, no module reload needed, sub-10ms updates [citation:5].
JavaScript: Module re-import with cache busting, dependency chain walking, 50-200ms updates [citation:1].
React: Fast Refresh preserves component state through instance/definition separation, updates only render functions [citation:7].
Vue: Separate handling for template (new render fn), script (option merging), and style (CSS HMR) [citation:1].
Svelte: Class replacement with instance preservation, maintains component state through registry pattern.
Despite these differences, all HMR implementations share the same underlying architecture: a WebSocket connection between server and client, a module graph on the server tracking dependencies, and the import.meta.hot API for modules to declare acceptance [citation:1]. The specialized behavior for different file types is implemented through Vite's plugin system—each plugin can hook into the build process and inject appropriate HMR handling code during transformation. This plugin-based architecture is what makes Vite's HMR system both powerful and extensible, supporting over 20 frameworks through community plugins [citation:4].